﻿using UnityEngine;
using UnityEngine.UI;
using System.Net;
using System.Net.Sockets;//引入socket命名空间
using System.Collections.Generic;
using System.Text;
using System.Threading;
using LitJson;
using System;

public class Broadcast
{
    static Broadcast instance;
    public static Broadcast Instance
    {
        get { return instance ?? (instance = new Broadcast()); }
    }

    public class ClientInfo
    {
        public string strHostName = string.Empty;
        public string strDerviceName = string.Empty;
        public string strIP = string.Empty;
    }
    static List<string> vecLocalIP = new List<string>();
    Thread threadSendBoradcast;

    UdpClient UDPrecv;
    Socket udpRecvSocket;
    /// <summary>
    /// 客户端IP列表
    /// </summary>
    public static Dictionary<string, ClientInfo> dicClient = new Dictionary<string, ClientInfo>();
    public static ClientInfo clientInfoSelf = new ClientInfo();
    public static string strDerviceName = string.Empty;
    static string strHostName = string.Empty;
    static Action<AppView.EMClientUpdate> clientChangeFun;
    static Action<MsgPanel.MsgInfo, string> talkFun;
    public void Init(Action<AppView.EMClientUpdate> clientFun, Action<MsgPanel.MsgInfo, string> talk)
    {
        clientChangeFun = clientFun;
        talkFun = talk;

        string ip = string.Empty;
        strDerviceName = SystemInfo.deviceName;
        strHostName = Dns.GetHostName();

        Debug.Log("Broadcast:: host: " + strHostName);
        foreach (var it in Dns.GetHostByName(Dns.GetHostName()).AddressList)
        {
            ip = it.ToString();
            if (ip.Length <= 15)
                vecLocalIP.Add(ip);
        }

        //dicClient.Add(vecLocalIP[0], new ClientInfo()
        //{
        //    strDerviceName = Broadcast.strDerviceName,
        //    strHostName = Broadcast.strHostName,
        //    strIP = vecLocalIP[0]
        //});
        //clientInfoSelf = dicClient[vecLocalIP[0]];
        clientInfoSelf = new ClientInfo()
        {
            strDerviceName = Broadcast.strDerviceName,
            strHostName = Broadcast.strHostName,
            strIP = vecLocalIP[0]
        };

        UDPrecv = new UdpClient();
        udpRecvSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        udpRecvSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
        udpRecvSocket.Bind(new IPEndPoint(IPAddress.Any, Common.BROADCAST_PORT));
        UDPrecv.Client = udpRecvSocket;
        UDPrecv.EnableBroadcast = true;
        UDPrecv.BeginReceive(ReceiveBroadCast, null);

        clientChangeFun?.Invoke(AppView.EMClientUpdate.Add);
        SyncClient();
    }

    int lSyncTime = 0;
    public void SyncClient()
    {
        if (lSyncTime != 0 && Common.GetNowTime() - lSyncTime < 20)
            return;

        lSyncTime = Common.GetNowTime();
        threadSendBoradcast = new Thread(SendBoradcast);
        threadSendBoradcast.Start();
    }

    /// <summary>
    /// 获取本地信息
    /// </summary>
    /// <returns></returns>
    public static string GetLocalInfoMsg()
    {
        JsonData data = new JsonData();
        data["msg"] = Protocol.EMProtocol.myInfo.ToString();
        data["host"] = strHostName;
        data["dervice"] = strDerviceName;

        return data.ToJson();
    }

    public void OnDestroy()
    {
        lock (dicClient)
        {
            JsonData json = new JsonData();
            json["msg"] = Protocol.EMProtocol.offline.ToString();
            json["host"] = strHostName;
            json["dervice"] = strDerviceName;
            string msg = json.ToJson();

            foreach (var it in dicClient)
            {
                SendUDPMsg(msg, it.Key);
            }
        }
        Debug.Log("Broadcast:: 发送离线消息");
        udpRecvSocket.Shutdown(SocketShutdown.Both);
        udpRecvSocket.Shutdown(SocketShutdown.Receive);
        udpRecvSocket.Close();
        UDPrecv.Close();
        threadSendBoradcast.Abort();
    }

    static void SendBoradcast()
    {
        lock (vecLocalIP)
        {
            Debug.Log("Broadcast:: 更新客户端");
            string msg = GetLocalInfoMsg();
            for (int i = 0; i < vecLocalIP.Count; i++)
            {
                var ips = vecLocalIP[i].Split("."[0]);
                for (int s = 254; s > 1; s--)
                {
                    string ip = ips[0] + "." + ips[1] + "." + ips[2] + "." + s.ToString();
                    SendUDPMsg(msg, ip);
                }
            }
        }
    }

    public static void SendUDPMsg(string msg, string ip)
    {
        if (!string.IsNullOrEmpty(msg))
        {
            try
            {
                UdpClient UDPscnd = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
                IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), Common.BROADCAST_PORT);

                byte[] data = Encoding.UTF8.GetBytes(msg);
                UDPscnd.Send(data, data.Length, iep);
                Debug.Log("Broadcast:: 发送消息:" + msg);
            }
            catch (System.Exception e)
            {
                Debug.LogError(e.Message);
            }
        }
    }

    /// <summary>
    /// 解析广播IP
    /// </summary>
    /// <param name="msg"></param>
    public static void ParseMsg(string msg, string ip)
    {
        if (msg[0] != '{')
            return;

        Debug.Log("Broadcast:: 处理广播消息: " + msg);

        try
        {
            JsonData jsonRecv = JsonMapper.ToObject(msg);

            if (!jsonRecv.Keys.Contains("msg"))
                return;

            if (Protocol.EMProtocol.talk.ToString() == jsonRecv["msg"].ToString())
            {
                var info = new MsgPanel.MsgInfo(jsonRecv["content"].ToString(), jsonRecv["send_time"].ToString(), jsonRecv["target"].ToString()
                  , jsonRecv["owner"].ToString(), jsonRecv["type"].ToString());
                talkFun?.Invoke(info, ip);

                lock (dicClient)
                {
                    lock (vecLocalIP)
                    {
                        if (!dicClient.ContainsKey(ip)
                      && !vecLocalIP.Contains(ip))
                        {
                            dicClient.Add(ip, new ClientInfo()
                            {
                                strDerviceName = jsonRecv["host"].ToString(),
                                strHostName = jsonRecv["dervice"].ToString(),
                                strIP = ip
                            });
                            clientChangeFun?.Invoke(AppView.EMClientUpdate.Add);

                            Debug.Log("Broadcast:: 给新客户端同步我的信息: " + ip);
                            UdpClient UDPscnd = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
                            IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), Common.BROADCAST_PORT);
                            msg = GetLocalInfoMsg();
                            if (!string.IsNullOrEmpty(msg))
                            {
                                byte[] data = Encoding.UTF8.GetBytes(msg);
                                UDPscnd.Send(data, data.Length, iep);
                            }
                        }
                    }
                }

                return;
            }

            lock (dicClient)
            {
                lock (vecLocalIP)
                {
                    if (jsonRecv["msg"].ToString() == Protocol.EMProtocol.offline.ToString())
                    {
                        lock (dicClient)
                        {
                            if (dicClient.ContainsKey(ip))
                            {
                                dicClient.Remove(ip);
                                clientChangeFun?.Invoke(AppView.EMClientUpdate.Del);
                            }
                        }
                        return;
                    }

                    if (dicClient.ContainsKey(ip))
                    {
                        if (jsonRecv["msg"].ToString() != Protocol.EMProtocol.hasYou.ToString())
                        {
                            Debug.Log("Broadcast:: 已经存在客户端信息, 给这个客户端同步我的信息: " + ip);
                            // 给这个客户端同步我的消息
                            JsonData json = new JsonData();
                            json["msg"] = Protocol.EMProtocol.hasYou.ToString();
                            json["host"] = strHostName;
                            json["dervice"] = strDerviceName;
                            msg = json.ToJson();
                            SendUDPMsg(msg, ip);
                        }
                    }
                    else if (!dicClient.ContainsKey(ip)
                        && !vecLocalIP.Contains(ip))
                    {
                        dicClient.Add(ip, new ClientInfo()
                        {
                            strDerviceName = jsonRecv["host"].ToString(),
                            strHostName = jsonRecv["dervice"].ToString(),
                            strIP = ip
                        });
                        clientChangeFun?.Invoke(AppView.EMClientUpdate.Add);

                        Debug.Log("Broadcast:: 给新客户端同步我的信息: " + ip);
                        UdpClient UDPscnd = new UdpClient(new IPEndPoint(IPAddress.Any, 0));
                        IPEndPoint iep = new IPEndPoint(IPAddress.Parse(ip), Common.BROADCAST_PORT);
                        msg = GetLocalInfoMsg();
                        if (!string.IsNullOrEmpty(msg))
                        {
                            byte[] data = Encoding.UTF8.GetBytes(msg);
                            UDPscnd.Send(data, data.Length, iep);
                        }
                    }
                }
            }
        }
        catch (System.Exception e)
        {
            Debug.Log(e.Message);
        }
    }

    void ReceiveBroadCast(System.IAsyncResult asyncResult)
    {
        IPEndPoint endPoint = null;

        byte[] buffer = UDPrecv.EndReceive(asyncResult, ref endPoint);
        string msg = Encoding.UTF8.GetString(buffer);
        Debug.Log("Broadcast:: 收到广播信息: " + msg + "=>" + endPoint.ToString());
        ParseMsg(msg, endPoint.ToString().Split(":"[0])[0]);
        UDPrecv.BeginReceive(new AsyncCallback(ReceiveBroadCast), null);
    }
}
